﻿using System.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using FarseerPhysics.DemoBaseXNA;
using FarseerPhysics.DemoBaseXNA.ScreenSystem;
using FarseerPhysics;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Dynamics.Contacts;
using FarseerPhysics.Dynamics.Joints;
using FarseerPhysics.Factories;
using FarseerPhysics.Collision;
using FarseerPhysics.Collision.Shapes;
using FarseerPhysics.Controllers;
using FarseerPhysics.Common;
using FarseerPhysics.Common.ConvexHull;
using FarseerPhysics.Common.Decomposition;
using FarseerPhysics.Common.PolygonManipulation;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;


namespace SimpleSamplesXNA
{
    //generates the objects in levels when painting with the brush
    public static class CreateObjects
    {
        public static void CreateBall(World world, Vector2 pos, float size, List<Body> bodies)
        {
            Fixture _ball;

            _ball = FixtureFactory.CreateCircle(world, size, 100f, new Vector2(pos.X, pos.Y));
            _ball.Body.BodyType = BodyType.Dynamic;
            _ball.Restitution = 0.768f;//bounce value
            _ball.Friction = 100f;
            bodies.Add(_ball.Body);
        }

        public static void CreatePlatform(World world, Vector2 pos, Vector2 size, List<Body> bodies)
        {
            Fixture _platform;

            _platform = FixtureFactory.CreateRectangle(world, size.X, size.Y, 1000f, pos);//CreateCircle(world, size * GloabalStates.shrinkFactor, 100f, new Vector2(pos.X * GloabalStates.shrinkFactor, pos.Y * GloabalStates.shrinkFactor));
            _platform.Body.BodyType = BodyType.Dynamic;
            _platform.Restitution = 0.768f;//bounce value
            _platform.Friction = 100f;
            bodies.Add(_platform.Body);
        }

        public static void CreateEdge(World world, Vector2 pos1, Vector2 pos2)
        {
            Fixture _edge;

            _edge = FixtureFactory.CreateEdge(world, pos1, pos2, 1f);//CreateRectangle(world, size.X, size.Y, 1000f);//CreateCircle(world, size * GloabalStates.shrinkFactor, 100f, new Vector2(pos.X * GloabalStates.shrinkFactor, pos.Y * GloabalStates.shrinkFactor));
            _edge.Body.BodyType = BodyType.Static;
            _edge.Restitution = 0.768f;//bounce value
            _edge.Friction = 100f;
        }
        
    }





    class LevelOne : GameScreen, IDemoScreen
    {
        AGameCalledPainting G;
        public bool isPlaying = false;
        public bool isPaused = false;
        public Vector2 BallPostion;
        public float BallRotation;
        Path path = new Path();
        List<Body> bodies = new List<Body>();

        private Fixture _ball = null;//game ball


        //data saved when putting objects to sleep
        private List<float> ADamp = new List<float>();
        private List<float> AVel = new List<float>();
        private List<float> Inert = new List<float>();
        private List<float> LDamp = new List<float>();
        private List<Vector2> LVel = new List<Vector2>();

        



        public void pauseLevel()
        {
            ADamp.Add(_ball.Body.AngularDamping);
            AVel.Add(_ball.Body.AngularVelocity);
            Inert.Add(_ball.Body.Inertia);
            LDamp.Add(_ball.Body.LinearDamping);
            LVel.Add(_ball.Body.LinearVelocity);

            _ball.Body.Awake = false;//put object to sleep
            _ball.Body.IgnoreGravity = true;
            

            foreach (Body b in bodies)//runs through each body in collection and sets them asleep and saves data
            {
                ADamp.Add(b.AngularDamping);
                AVel.Add(b.AngularVelocity);
                Inert.Add(b.Inertia);
                LDamp.Add(b.LinearDamping);
                LVel.Add(b.LinearVelocity);

                b.Awake = false;
                b.IgnoreGravity = true;
            }

            isPaused = true;

        }



        public void unPauseLevel()
        {
            _ball.Body.Awake = true;
            _ball.Body.IgnoreGravity = false;

            _ball.Body.AngularDamping = ADamp.ElementAt(0);
            ADamp.RemoveAt(0);
            _ball.Body.AngularVelocity = AVel.ElementAt(0);
            AVel.RemoveAt(0);
            _ball.Body.Inertia = Inert.ElementAt(0);
            Inert.RemoveAt(0);
            _ball.Body.LinearDamping = LDamp.ElementAt(0);
            LDamp.RemoveAt(0);
            _ball.Body.LinearVelocity = LVel.ElementAt(0);
            LVel.RemoveAt(0);

            foreach (Body b in bodies)//awakes objects and restores data
            {
                b.Awake = true;
                b.IgnoreGravity = false;

                b.AngularDamping = ADamp.ElementAt(0);
                ADamp.RemoveAt(0);
                b.AngularVelocity = AVel.ElementAt(0);
                AVel.RemoveAt(0);
                b.Inertia = Inert.ElementAt(0);
                Inert.RemoveAt(0);
                b.LinearDamping = LDamp.ElementAt(0);
                LDamp.RemoveAt(0);
                b.LinearVelocity = LVel.ElementAt(0);
                LVel.RemoveAt(0);
            }
            isPaused = false;
        }


        //create in game objects realtime--------------------------------------
        public void CreateBall(Vector2 pos, float size)
        {
            
            CreateObjects.CreateBall(this.World, pos, size, bodies);
            
        }

        public void CreatePlatform(Vector2 pos, Vector2 size)
        {

            CreateObjects.CreatePlatform(this.World, pos, size, bodies);

        }

        public void CreateEdge(Vector2 pos1, Vector2 pos2)
        {

            CreateObjects.CreateEdge(this.World,pos1,pos2);

        }
        //----------------------------------------------------------------------



        public LevelOne(AGameCalledPainting g)
        {
            G = g;
        }


        public string GetTitle()
        {
            return "Level 1: A Single Ball";
        }

        public string GetDetails()
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("This demo starts off with a single 'non-bouncing'\n falling ball.");
            sb.AppendLine("Right click to open object menu.");
            sb.AppendLine(string.Empty);
            sb.AppendLine("GamePad:");
            sb.AppendLine("  -Rotate: left and right triggers");
            sb.AppendLine("  -Move: left thumbstick");
            sb.AppendLine(string.Empty);
            sb.AppendLine("Keyboard:");
            sb.AppendLine("  -Rotate: left and right arrows");
            sb.AppendLine("  -Move: A,S,D,W");
            return sb.ToString();
        }

        

        public override void Initialize()
        {
            World = new World(new Vector2(0.0f, -9.8f));//gravity for level
            FarseerPhysics.DemoBaseXNA.BorerState.borderState = false;

            //build screen platform
            List<Vertices> borders = new List<Vertices>(4);
            borders.Add(PolygonTools.CreateRectangle(12, 1, new Vector2(0, -10), 0));
            FixtureFactory.CreateCompundPolygon(World, borders, 1);

            //Map texture to object in game
            //--------------------------------------------------------------------------------------------------
            //Microsoft.Xna.Framework.Graphics.Texture2D polygonTexture = G.loadTexture2d("Pictures\\Bubble");
            //uint[] data = new uint[polygonTexture.Width * polygonTexture.Height];
            //polygonTexture.GetData(data);
            //Vertices verts = PolygonTools.CreatePolygon(data, polygonTexture.Width, polygonTexture.Height, true);
            //Vector2 scale = new Vector2(0.07f, 0.07f);
            //verts.Scale(ref scale);
            //List<Vertices> _list = BayazitDecomposer.ConvexPartition(verts);
            //List<Fixture> compund = FixtureFactory.CreateCompundPolygon(World, _list, 1);
            //compund[0].Body.BodyType = BodyType.Dynamic;
            //-----------------------------------------------------------------------------------------------------
            isPlaying = true;
            GloabalStates.currentGameState = GloabalStates.CurrentGameStateenum.LevelOne;
            
            base.Initialize();
        }


        public override void LoadContent()
        {
            

            _ball = FixtureFactory.CreateCircle(World, 0.79f * GloabalStates.shrinkFactor, 100f, new Vector2(-23 * GloabalStates.shrinkFactor, 20 * GloabalStates.shrinkFactor));
            _ball.Body.BodyType = BodyType.Dynamic;
            _ball.Restitution = 0.768f;//bounce value
            _ball.Friction = 100f;

            //creates line
            Fixture f1 = FixtureFactory.CreateEdge(World, new Vector2(0, 0), new Vector2(25 * GloabalStates.shrinkFactor, 25 * GloabalStates.shrinkFactor), 1f);
            f1.Body.BodyType = BodyType.Static;
            f1.Restitution = 0.768f;//bounce value
            f1.Friction = 100f;
            
            
            //creates fixture for sprite drawn over ball skeleton
            //Vertices box = PolygonTools.CreateRectangle(.5f, .5f, new Vector2(0, 0), 0);
            //PolygonShape quarter1 = new PolygonShape(box);
            //_ball.Body.CreateFixture(quarter1);

            //--------------------------------------------------------------------------------
            //We define the path for circle ball
                //path = new Path();
                //path.Add(new Vector2(0, 10));
                //path.Add(new Vector2(2.5f, 7.5f));
                //path.Add(new Vector2(10, 9));
                //path.Add(new Vector2(7.5f, 0.5f));
                //path.Add(new Vector2(-2.5f, 7));
                //path.Closed = true;

            //It consists of two shapes: a rectangle and a circle
                //List<Shape> shapes = new List<Shape>(2);
                //shapes.Add(new PolygonShape(PolygonTools.CreateRectangle(0.65f, 0.65f, new Vector2(-0.1f, 0), 0)));
                //shapes.Add(new CircleShape(0.5f));
            
            //We distribute the shapes along the path
                //bodies = PathManager.EvenlyDistibuteShapesAlongPath(World, path, shapes, BodyType.Dynamic, 20, 40);
            

            //Attach the bodies together with revolute joints
                //PathManager.AttachBodiesWithRevoluteJoint(World, bodies, new Vector2(0, 0.5f), new Vector2(0, -0.5f), true, true);
            //------------------------------------------------------------------------------------
            
            base.LoadContent();
        }



        public override void HandleInput(InputState input)
        {
            

            if (input.CurrentGamePadState.IsConnected)
            {
                HandleGamePadInput(input);
            }
            else
            {
                HandleKeyboardInput(input);
            }

            base.HandleInput(input);
        }


        private void HandleGamePadInput(InputState input)
        {
            //Vector2 force = 50 * input.CurrentGamePadState.ThumbSticks.Left;
            //_ball.Body.ApplyForce(force);

            //float rotation = 40 * input.CurrentGamePadState.Triggers.Left;
            //_ball.Body.ApplyTorque(rotation);

            //rotation = -40 * input.CurrentGamePadState.Triggers.Right;
            //_ball.Body.ApplyTorque(rotation);
        }


        //called when _ball position changes with respect to time
        public void updateBall()
        {
            if (_ball != null)
            {
                BallPostion = _ball.Body.Position;
                BallRotation = _ball.Body.Rotation;
                
            }
        }

        

        private void HandleKeyboardInput(InputState input)
        {
            //const float forceAmount = 70;
            //Vector2 force = Vector2.Zero;
            //force.Y = -force.Y;
            
            

            //if (input.CurrentKeyboardState.IsKeyDown(Keys.A))
            //{
            //    force += new Vector2(-forceAmount, 0);
            //}
            //if (input.CurrentKeyboardState.IsKeyDown(Keys.S))
            //{
            //    force += new Vector2(0, -forceAmount);
            //}
            //if (input.CurrentKeyboardState.IsKeyDown(Keys.D))
            //{
            //    force += new Vector2(forceAmount, 0);
            //}
            //if (input.CurrentKeyboardState.IsKeyDown(Keys.W))
            //{
            //    force += new Vector2(0, forceAmount);
            //}

            ////_ball.Body.ApplyForce(force);

            //const float torqueAmount = 40;
            //float torque = 0;

            //if (input.CurrentKeyboardState.IsKeyDown(Keys.Left))
            //{
            //    torque += torqueAmount;
            //}
            //if (input.CurrentKeyboardState.IsKeyDown(Keys.Right))
            //{
            //    torque -= torqueAmount;
            //}

            ////_ball.Body.ApplyTorque(torque);
        }

    }
}
